package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.AlertRuleDto;
import com.zzyl.entity.AlertRule;
import com.zzyl.mapper.AlertRuleMapper;
import com.zzyl.service.AlertRuleService;
import com.zzyl.vo.AlertRuleVo;
import com.zzyl.vo.DeviceDataVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author sjqn
 */
@Slf4j
@Service
public class AlertRuleServiceImpl implements AlertRuleService {

    @Autowired
    private AlertRuleMapper alertRuleMapper;

    /**
     * 新增报警规则
     *
     * @param alertRuleDto 报警规则请求模型
     */
    @Override
    public void createRule(AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.insert(alertRule);
    }

    /**
     * 分页条件查询
     *
     * @param pageNum       页码
     * @param pageSize      页面大小
     * @param alertRuleName 报警规则名称
     * @param productKey    产品key
     * @param functionName  功能名称
     * @return 分页结果
     */
    @Override
    public PageResponse<AlertRuleVo> getAlertRulePage(Integer pageNum, Integer pageSize, String alertRuleName,
                                                      String productKey, String functionName) {
        PageHelper.startPage(pageNum, pageSize);
        Page<AlertRuleVo> page = alertRuleMapper.page(alertRuleName, productKey, functionName);
        //添加提示
        page.getResult().forEach(alertRuleVo -> {
            String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRuleVo.getFunctionName(),
                    alertRuleVo.getOperator(), alertRuleVo.getValue(), alertRuleVo.getDuration());
            alertRuleVo.setRules(alertReason);
        });

        return PageResponse.of(page, AlertRuleVo.class);
    }

    /**
     * 删除报警
     *
     * @param id 报警规则id
     */
    @Override
    public void deleteAlertRule(Long id) {
        alertRuleMapper.deleteByPrimaryKey(id);
    }

    /**
     * 修改报警
     *
     * @param id           报警规则id
     * @param alertRuleDto 报警规则更新请求模型
     */
    @Override
    public void updateAlertRule(Long id, AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.updateByPrimaryKeySelective(alertRule);
    }

    /**
     * 查询报警信息
     *
     * @param id 报警规则id
     * @return 报警信息
     */
    @Override
    public AlertRuleVo readAlertRule(Long id) {
        AlertRule alertRule = alertRuleMapper.selectByPrimaryKey(id);
        return BeanUtil.toBean(alertRule, AlertRuleVo.class);
    }

    /**
     * 启用或禁用
     *
     * @param id     报警id
     * @param status 报警规则状态
     */
    @Override
    public void enableOrDisable(Long id, Integer status) {
        alertRuleMapper.updateStatus(id, status);
    }


    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 报警通知
     */
    @Override
    @Scheduled(cron = "0/5 * * * * ?")
    public void scheduled() {
        // 1. 查询所有报警规则
        List<AlertRuleVo> alertRuleVos = queryAlertRule();
        // 2. 判断报警规则是否为空
        // 2.1 如果为空，直接结束
        if (ObjectUtil.isEmpty(alertRuleVos)) {
            return;
        }
        // 2.2 如果非空，查询所有设备上报的最新数据(从redis缓存中获得)
        List<String> deviceDataValues = redisTemplate.<String, String>opsForHash().values(CacheConstants.DEVICE_LASTDATA_CACHE_KEY);
        // 3. 判断查询到的数据是否为空
        // 3.1 如果为空，直接结束
        if (ObjectUtil.isEmpty(deviceDataValues)) {
            return;
        }
        // 3.2 如果非空，将数据转换为DeviceDataVo并且用集合装起来
        Map<String, List<DeviceDataVo>> deviceDataMap = new HashMap<>();
        for (String json : deviceDataValues) {
            List<DeviceDataVo> deviceDataVos = JSONUtil.toList(json, DeviceDataVo.class);
            if (CollUtil.isNotEmpty(deviceDataVos)) {
                for (DeviceDataVo deviceDataVo : deviceDataVos) {
                    // 将每个设备的功能数据加入 map 中对应 iotId 的列表
                    deviceDataMap.computeIfAbsent(deviceDataVo.getIotId(), k -> new ArrayList<>()).add(deviceDataVo);
                }
            }
        }
        log.info("查询到的设备数据：{}", deviceDataMap);
        // 4. 遍历数据集合，对每条数据进行校验
        // 4.1 如果设备数据上报时间大于1min，直接结束（数据无效）

        // 4.2 如果小于1min，则对设备的报警规则进行判断

        // 4.2.1 如果不符合规则，直接结束

        // 4.2.2 如果符合规则，封装进符合规则的集合中

        // 5. 遍历符合规则的集合，对这些数据进行校验
        // 5.1 判断数据的上报时间是否在生效时间内
        // 5.1.1 如果不在，直接结束

        // 5.1.2 如果在，判断数据阈值


        // 5.2 判断数据是否达到阈值
        // 5.2.1 如果没有达到阈值，直接结束，并且将redis的报警数设置为0

        // 5.2.2 如果达到阈值，判断是否在静默时间内

        // 5.3 判断数据是否在禁默周期
        // 5.3.0 查询redis中禁默周期

        // 5.3.1 如果在禁默周期，表示还没到报警时间，直接结束，

        // 5.3.2 如果不在，判断是否在生效时间内

        // 5.4 判断报警次数是否等于持续周期
        // 5.4.1 如果报警次数没有达到持续周期，redis中的报警次数+1

        // 5.4.2 如果报警次数等于持续周期，触发警报

        // 6. 触发警报
        // 6.1 将redis中的报警数设置为0

        // 6.2 添加redis的沉默周期(过期令牌)

        // 6.3 判断该数据是否是老人异常
        // 6.3.1 如果不是，查询维修员ID

        // 6.3.11  超级管理员发送通知给维修员

        // 6.3.12  批量保存报警数据

        // 6.3.2 如果是，判断数据是否为设备异常数据
        // 6.3.21 如果不是，那该设备为随身设备，查询该老人的护理员

        // 6.3.22  超级管理员发送通知给护理员

        // 6.3.23  批量保存报警数据

        // 6.3.31 如果是设备异常数据，那该设备为固定设备，查询该床位的老人护理员

        // 6.3.32  超级管理员发送通知给护理员

        // 6.3.33  批量保存报警数据

    }


    public List<AlertRuleVo> queryAlertRule() {
        List<AlertRuleVo> alertRuleVos = alertRuleMapper.queryAlertRule();
        return alertRuleVos;
    }


}
